### misc generators

def restartable_generator(f):
	class _restartable(object):
		def __init__(self,*args,**kwargs):
			self._args=args
			self._kwargs=kwargs
		def __iter__(self):
			return f(*self._args,**self._kwargs)
	_restartable.__name__=f.__name__
	return _restartable

@restartable_generator
def outer(*lists):
	if len(lists)==0:
		yield ()
	else:
		for a in lists[0]:
			for b in outer(*lists[1:]):
				yield (a,)+b

@restartable_generator
def choose(l,k):
	if k==0:
		yield ()
	else:
		for i in xrange(len(l)):
			for b in choose(l[i+1:],k-1):
				yield (l[i],)+b

@restartable_generator
def permutations(l):
	l=list(l)
	if len(l)==0:
		yield ()
	else:
		for i in range(len(l)):
			for b in permutations(l[:i]+l[i+1:]):
				yield (l[i],)+b

@restartable_generator
def xzip(l0,*lists):
	ii=[iter(l) for l in (l0,)+lists]
	while True:
		t=()
		for i in ii:
			try:
				t+=(i.next(),)
			except StopIteration:
				return
		yield t

@restartable_generator
def concat(*lists):
	for l in lists:
		for x in l:
			yield x

@restartable_generator
def iota():
	i=0
	while True:
		yield i
		i+=1

@restartable_generator
def take(g,n):
	if n<=0: return
	for x in g:
		yield x
		n-=1
		if n<=0: return
